/* ----------
 * pgstat_internal.h
 *
 * PostgreSQL 累计统计系统的定义，仅应由实现统计支持的文件
 * 使用，而不是报告/查询统计的文件。
 *
 * Copyright (c) 2001-2022, PostgreSQL Global Development Group
 *
 * src/include/utils/pgstat_internal.h
 * ----------
 */
#ifndef PGSTAT_INTERNAL_H
#define PGSTAT_INTERNAL_H


#include "common/hashfn.h"
#include "lib/dshash.h"
#include "lib/ilist.h"
#include "pgstat.h"
#include "storage/lwlock.h"
#include "utils/dsa.h"


/*
 * 与统计信息的共享内存存储相关的类型。
 *
 * 每个对象的统计信息存储在“共享统计”哈希表中。该表的条目（PgStatShared_HashEntry）包含指向对象的实际统计数据的指针（统计数据的大小因统计类型而异）。该表的键由PgStat_HashKey提供。
 *
 * 一旦后端引用了共享统计条目，它将递增条目的引用计数。即使在统计数据被删除（例如，由于DROP TABLE）后，条目本身只能在所有引用被释放后才可以删除。
 *
 * 这些引用计数结合后端本地哈希表（pgStatEntryRefHash，其中条目指向PgStat_EntryRef）放在共享哈希表前面，意味着大多数统计工作可以在不接触共享哈希表的情况下进行，从而减少争用。
 *
 * 一旦存在等待的统计更新，将为表分配PgStat_EntryRef->pending以容纳尚未应用的统计更新的工作空间。一旦统计数据被刷新，PgStat_EntryRef->pending将被释放。
 *
 * 共享哈希表中的每种统计类型都有一个固定的成员PgStatShared_Common作为第一个元素。
 */

/* 共享统计哈希条目键的结构。 */
typedef struct PgStat_HashKey
{
	PgStat_Kind kind;			/* 统计条目类型 */
	Oid			dboid;			/* 数据库ID。共享对象为InvalidOid。 */
	Oid			objoid;			/* 对象ID，可能是表或函数。 */
} PgStat_HashKey;

/*
 * 共享统计哈希条目。它本身不包含任何统计信息，但指向它们（通过->body）。这允许统计条目本身具有可变大小。
 */
typedef struct PgStatShared_HashEntry
{
	PgStat_HashKey key;			/* 哈希键 */

	/*
	 * 如果设置了dropped，后端需要释放其引用，以便可以释放条目的内存。一旦被标记为已删除，就不允许创建新的引用。
	 */
	bool		dropped;

	/*
	 * 管理条目自身生命周期的引用计数（与指向它的 dshash 条目相对）。统计生命周期必须与哈希表条目生命周期分开，因为我们允许后端指向统计条目而无需持有哈希表锁（以及其他一些原因）。
	 *
	 * 只要条目没有被删除，就会向引用计数中添加 1，以表示该条目不应被删除。此外，每个引用该条目的后端只要持有引用，就需要增加引用计数。
	 *
	 * 只能在持有至少对包含条目的 dshash 分区的共享锁时增加/减小。它需要是一个原子变量，因为多个后端可以仅通过共享锁来增加引用计数。
	 *
	 * 当引用计数达到 0 时，需要释放该条目。
	 */
	pg_atomic_uint32 refcount;

	/*
	 * 跟踪条目被重用次数的计数器。
	 *
	 * 在创建条目时设置为 0，并在每次使用 pgstat_reinit_entry() 重新初始化共享条目时增加 1。
	 *
	 * 只能在持有至少对包含条目的 dshash 分区的共享锁时增加/减小。像引用计数一样，它需要是一个原子变量，因为多个后端可以仅通过共享锁来增加生成值。
	 */
	pg_atomic_uint32 generation;

	/*
	 * 指向共享统计信息的指针。统计条目始终以 PgStatShared_Common 开头，嵌入在包含 PgStat_Kind 特定统计字段的更大结构中。
	 */
	dsa_pointer body;
} PgStatShared_HashEntry;

/*
 * PgStatShm_Stat*Entry 的公共头部结构。
 */
typedef struct PgStatShared_Common
{
	uint32		magic;			
/* 仅作为有效性交叉检查 */
	/* 锁保护统计内容（即头部后数据） */
	LWLock		lock;
} PgStatShared_Common;

/*
 * 对共享统计条目的后端本地引用。只要存在至少一个
 * 这样的引用，共享统计条目将不会被释放。
 *
 * 如果有待处理的共享统计更新，这些将存储在
 * ->pending。
 */
typedef struct PgStat_EntryRef
{
	/*
	 * 指向共享统计哈希表中 PgStatShared_HashEntry 条目的指针。
	 */
	PgStatShared_HashEntry *shared_entry;

	/*
	 * 指向统计数据（即 PgStatShared_HashEntry->body）的指针，以本地指针的方式解决，
	 * 以避免重复调用 dsa_get_address()。
	 */
	PgStatShared_Common *shared_stats;

	/*
	 * PgStatShared_HashEntry->generation 的副本，保持本地跟踪
	 * 检索到的共享统计条目的“生成”（重用次数）。
	 */
	uint32		generation;

	/*
	 * 待处理的统计数据，最终需要刷新到共享内存
	 * 统计信息中。每种使用待处理数据的统计类型定义其
	 * 待处理数据的格式，并需要提供一个
	 * PgStat_KindInfo->flush_pending_cb 回调来将待处理数据合并到共享
	 * 统计信息中。
	 */
	void	   *pending;
	dlist_node	pending_node;	/* 在 pgStatPending 列表中的成员资格 */
} PgStat_EntryRef;


/*
 * 一些统计变化是事务性的。为了维护这些，维护一个
 * PgStat_SubXactStatus 条目的栈，包含与当前事务及其活动子事务
 * 相关的数据。
 */
typedef struct PgStat_SubXactStatus
{
	int			nest_level;		/* 子事务嵌套级别 */

	struct PgStat_SubXactStatus *prev;	/* 如果有更高层级的子事务 */

	/*
	 * 对于事务性删除的对象，统计信息也需要
	 * 以事务方式删除。收集在当前（子）事务中删除的统计信息，
	 * 仅在知道事务提交/中止时才执行统计信息删除。
	 * 为了处理副本和崩溃，统计信息删除包含在提交/中止记录中。
	 */
	dlist_head	pending_drops;
	int			pending_drops_count;

	/*
	 * 开放事务的元组插入/删除计数在确认是
	 * 将提交还是中止之前，不能传播到 PgStat_TableStatus 计数器中。
	 * 因此，我们将这些计数保存在每个子事务的
	 * 结构中，这些结构驻留在 TopTransactionContext 中。
	 * 这个数据结构是基于假设子事务通常不会修改很多
	 * 表的前提设计的。
	 */
	PgStat_TableXactStatus *first;	/* 本子事务的列表头 */
} PgStat_SubXactStatus;


/*
 * 特定类型统计信息的元数据。
 */
typedef struct PgStat_KindInfo
{
	/*
	 * 是否为这种类型的统计信息存在固定数量的统计对象（例如
	 * bgwriter 统计）或不存在（例如表）。
	 */
	bool		fixed_amount:1;

	/*
	 * 这种类型的统计信息是否可以从另一个数据库访问？决定
	 * 一个统计对象是否被包含在统计快照中。
	 */
	bool		accessed_across_databases:1;

	/*
	 * 对于可变数量的统计信息：通过名称在磁盘上识别，而不是
	 * PgStat_HashKey。可能只需要用于复制槽统计。
	 */
	bool		named_on_disk:1;

	/*
	 * 共享统计哈希表中条目的大小（由
	 * PgStatShared_HashEntry->body 指向）。
	 */
	uint32		shared_size;

	/*
	 * 位于共享统计条目内部的统计信息的偏移量/大小。
	 * 在分别从磁盘[去]序列化统计信息时使用。
	 * 与 shared_size 分开，因为[去]序列化可能不包括像 lwlocks 一样的
	 * 内存状态。
	 */
	uint32		shared_data_off;
	uint32		shared_data_len;

	/*
	 * 这种类型的待处理数据的大小。例如，PgStat_EntryRef->pending 的大小。
	 * 用于分配。
	 *
	 * 0 表示这种类型的条目绝不应有待处理条目。
	 */
	uint32		pending_size;

	/*
	 * 对于可变数量的统计信息：刷新待处理的统计信息。
	 * 如果使用待处理数据，则必需。
	 */
	bool		(*flush_pending_cb) (PgStat_EntryRef *sr, bool nowait);

	/*
	 * 对于可变数量的统计信息：删除待处理的统计信息。
	 * 可选。
	 */
	void		(*delete_pending_cb) (PgStat_EntryRef *sr);

	/*
	 * 对于可变数量的统计信息：重置重置时间戳。
	 * 可选。
	 */
	void		(*reset_timestamp_cb) (PgStatShared_Common *header, TimestampTz ts);

	/*
	 * 对于具有 named_on_disk 的可变数量统计信息。可选。
	 */
	void		(*to_serialized_name) (const PgStat_HashKey *key,
									   const PgStatShared_Common *header, NameData *name);
	bool		(*from_serialized_name) (const NameData *name, PgStat_HashKey *key);

	/*
	 * 对于固定数量的统计信息：重置所有。
	 */
	void		(*reset_all_cb) (TimestampTz ts);

	/*
	 * 对于固定数量的统计信息：为条目构建快照。
	 */
	void		(*snapshot_cb) (void);

	/* 统计信息种类的名称 */
	const char *const name;
} PgStat_KindInfo;


/*
 * 我们为其保留统计信息的 SLRU 名称列表。
 * SLRU 没有中央注册表，因此我们使用这个固定列表。
 * “其他”条目用于所有没有明确条目的 SLRU（例如扩展中的 SLRU）。
 *
 * 这里只定义它，以便后续类型定义时已知 SLRU_NUM_ELEMENTS。
 */
static const char *const slru_names[] = {
	"CommitTs",
	"MultiXactMember",
	"MultiXactOffset",
	"Notify",
	"Serial",
	"Subtrans",
	"Xact",
	"other"						/* 必须是最后一个 */
};

#define SLRU_NUM_ELEMENTS	lengthof(slru_names)


/* ----------
 * 不同类型固定数量统计信息的类型和定义。
 *
 * 单写者统计使用变更计数机制实现低开销
 * 写入——显然它们比读取更具性能关键。查看
 * struct PgBackendStatus 的定义以了解变更计数机制的一些解释。
 *
 * 因为重置单写者统计的明显实现与此不兼容（另一个后端需要写入），
 * 我们在重置时不会修改共享统计信息。相反，仅在
 * 统计数据的副本中记录当前计数器值，该副本受到 ->lock 保护。
 * 请参见 pgstat_fetch_stat_(archiver|bgwriter|checkpointer) 的读取侧。
 *
 * 唯一的例外是这些结构中的 stat_reset_timestamp，
 * 受到 ->lock 保护，因为它必须由另一个后端在重置时写入。
 * ----------
 */

typedef struct PgStatShared_Archiver
{
	/* 锁保护 ->reset_offset 以及 stats->stat_reset_timestamp */
	LWLock		lock;
	uint32		changecount;
	PgStat_ArchiverStats stats;
	PgStat_ArchiverStats reset_offset;
} PgStatShared_Archiver;

typedef struct PgStatShared_BgWriter
{
	/* 锁保护 ->reset_offset 以及 stats->stat_reset_timestamp */
	LWLock		lock;
	uint32		changecount;
	PgStat_BgWriterStats stats;
	PgStat_BgWriterStats reset_offset;
} PgStatShared_BgWriter;

typedef struct PgStatShared_Checkpointer
{
	/* 锁保护 ->reset_offset 以及 stats->stat_reset_timestamp */
	LWLock		lock;
	uint32		changecount;
	PgStat_CheckpointerStats stats;
	PgStat_CheckpointerStats reset_offset;
} PgStatShared_Checkpointer;

typedef struct PgStatShared_SLRU
{
	/* 锁保护 ->stats */
	LWLock		lock;
	PgStat_SLRUStats stats[SLRU_NUM_ELEMENTS];
} PgStatShared_SLRU;

typedef struct PgStatShared_Wal
{
	/* 锁保护 ->stats */
	LWLock		lock;
	PgStat_WalStats stats;
} PgStatShared_Wal;





typedef struct PgStatShared_Database
{
	PgStatShared_Common header;
	PgStat_StatDBEntry stats;
} PgStatShared_Database;

typedef struct PgStatShared_Relation
{
	PgStatShared_Common header;
	PgStat_StatTabEntry stats;
} PgStatShared_Relation;

typedef struct PgStatShared_Function
{
	PgStatShared_Common header;
	PgStat_StatFuncEntry stats;
} PgStatShared_Function;

typedef struct PgStatShared_Subscription
{
	PgStatShared_Common header;
	PgStat_StatSubEntry stats;
} PgStatShared_Subscription;

typedef struct PgStatShared_ReplSlot
{
	PgStatShared_Common header;
	PgStat_StatReplSlotEntry stats;
} PgStatShared_ReplSlot;


/*
 * 中央共享内存条目用于累计统计系统。
 *
 * 固定数量的统计数据，动态共享内存哈希表用于
 * 非固定数量的统计数据，以及剩余的细节都
 * 从这里访问。
 */
typedef struct PgStat_ShmemControl
{
	void	   *raw_dsa_area;

	/*
	 * 可变编号对象的统计数据保存在这个共享哈希表中。
	 * 详见 PgStat_Kind 以上的注释。
	 */
	dshash_table_handle hash_handle;	/* 共享的 dbstat 哈希 */

	/* 统计系统是否已经关闭？只是一个调试检查。 */
	bool		is_shutdown;

	/*
	 * 每当无法释放已丢弃对象的统计数据 - 因为
	 * 后端仍然有引用 - 丢弃的后端调用
	 * pgstat_request_entry_refs_gc() 增加这个计数器。最终
	 * 这导致后端运行 pgstat_gc_entry_refs()，允许内存
	 * 被回收。
	 */
	pg_atomic_uint64 gc_request_count;

	/*
	 * 固定编号对象的统计数据。
	 */
	PgStatShared_Archiver archiver;
	PgStatShared_BgWriter bgwriter;
	PgStatShared_Checkpointer checkpointer;
	PgStatShared_SLRU slru;
	PgStatShared_Wal wal;
} PgStat_ShmemControl;


/*
 * 缓存的统计快照
 */
typedef struct PgStat_Snapshot
{
	PgStat_FetchConsistency mode;

	/* 快照拍摄时的时间 */
	TimestampTz snapshot_timestamp;

	bool		fixed_valid[PGSTAT_NUM_KINDS];

	PgStat_ArchiverStats archiver;

	PgStat_BgWriterStats bgwriter;

	PgStat_CheckpointerStats checkpointer;

	PgStat_SLRUStats slru[SLRU_NUM_ELEMENTS];

	PgStat_WalStats wal;

	/* 批量释放快照 */
	MemoryContext context;
	struct pgstat_snapshot_hash *stats;
} PgStat_Snapshot;


/*
 * 收集后端本地统计状态。
 */
typedef struct PgStat_LocalState
{
	PgStat_ShmemControl *shmem;
	dsa_area   *dsa;
	dshash_table *shared_hash;

	/* 当前的统计快照 */
	PgStat_Snapshot snapshot;
} PgStat_LocalState;


/*
 * 下面进一步定义的内联函数。
 */

static inline void pgstat_begin_changecount_write(uint32 *cc);
static inline void pgstat_end_changecount_write(uint32 *cc);
static inline uint32 pgstat_begin_changecount_read(uint32 *cc);
static inline bool pgstat_end_changecount_read(uint32 *cc, uint32 cc_before);

static inline void pgstat_copy_changecounted_stats(void *dst, void *src, size_t len,
												   uint32 *cc);

static inline int pgstat_cmp_hash_key(const void *a, const void *b, size_t size, void *arg);
static inline uint32 pgstat_hash_hash_key(const void *d, size_t size, void *arg);
static inline size_t pgstat_get_entry_len(PgStat_Kind kind);
static inline void *pgstat_get_entry_data(PgStat_Kind kind, PgStatShared_Common *entry);


/*
 * pgstat.c 中的函数
 */

extern const PgStat_KindInfo *pgstat_get_kind_info(PgStat_Kind kind);

#ifdef USE_ASSERT_CHECKING
extern void pgstat_assert_is_up(void);
#else
#define pgstat_assert_is_up() ((void)true)
#endif

extern void pgstat_delete_pending_entry(PgStat_EntryRef *entry_ref);
extern PgStat_EntryRef *pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, Oid objoid, bool *created_entry);
extern PgStat_EntryRef *pgstat_fetch_pending_entry(PgStat_Kind kind, Oid dboid, Oid objoid);

extern void *pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, Oid objoid);
extern void pgstat_snapshot_fixed(PgStat_Kind kind);


/*
 * pgstat_archiver.c 中的函数
 */

extern void pgstat_archiver_reset_all_cb(TimestampTz ts);
extern void pgstat_archiver_snapshot_cb(void);


/*
 * pgstat_bgwriter.c中的函数
 */

extern void pgstat_bgwriter_reset_all_cb(TimestampTz ts);
extern void pgstat_bgwriter_snapshot_cb(void);


/*
 * pgstat_checkpointer.c中的函数
 */

extern void pgstat_checkpointer_reset_all_cb(TimestampTz ts);
extern void pgstat_checkpointer_snapshot_cb(void);


/*
 * pgstat_database.c中的函数
 */

extern void pgstat_report_disconnect(Oid dboid);
extern void pgstat_update_dbstats(TimestampTz ts);
extern void AtEOXact_PgStat_Database(bool isCommit, bool parallel);

extern PgStat_StatDBEntry *pgstat_prep_database_pending(Oid dboid);
extern void pgstat_reset_database_timestamp(Oid dboid, TimestampTz ts);
extern bool pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
extern void pgstat_database_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts);


/*
 * pgstat_function.c中的函数
 */

extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);


/*
 * pgstat_relation.c中的函数
 */

extern void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit);
extern void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);

extern bool pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
extern void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref);


/*
 * pgstat_replslot.c中的函数
 */

extern void pgstat_replslot_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts);
extern void pgstat_replslot_to_serialized_name_cb(const PgStat_HashKey *key, const PgStatShared_Common *header, NameData *name);
extern bool pgstat_replslot_from_serialized_name_cb(const NameData *name, PgStat_HashKey *key);


/*
 * 在 pgstat_shmem.c 中的函数
 */

extern void pgstat_attach_shmem(void);
extern void pgstat_detach_shmem(void);

extern PgStat_EntryRef *pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, Oid objoid,
											 bool create, bool *found);
extern bool pgstat_lock_entry(PgStat_EntryRef *entry_ref, bool nowait);
extern bool pgstat_lock_entry_shared(PgStat_EntryRef *entry_ref, bool nowait);
extern void pgstat_unlock_entry(PgStat_EntryRef *entry_ref);
extern bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, Oid objoid);
extern void pgstat_drop_all_entries(void);
extern PgStat_EntryRef *pgstat_get_entry_ref_locked(PgStat_Kind kind, Oid dboid, Oid objoid,
													bool nowait);
extern void pgstat_reset_entry(PgStat_Kind kind, Oid dboid, Oid objoid, TimestampTz ts);
extern void pgstat_reset_entries_of_kind(PgStat_Kind kind, TimestampTz ts);
extern void pgstat_reset_matching_entries(bool (*do_reset) (PgStatShared_HashEntry *, Datum),
										  Datum match_data,
										  TimestampTz ts);

extern void pgstat_request_entry_refs_gc(void);
extern PgStatShared_Common *pgstat_init_entry(PgStat_Kind kind,
											  PgStatShared_HashEntry *shhashent);


/*
 * pgstat_slru.c中的函数
 */

extern bool pgstat_slru_flush(bool nowait);
extern void pgstat_slru_reset_all_cb(TimestampTz ts);
extern void pgstat_slru_snapshot_cb(void);


/*
 * pgstat_wal.c中的函数
 */

extern bool pgstat_flush_wal(bool nowait);
extern void pgstat_init_wal(void);
extern bool pgstat_have_pending_wal(void);

extern void pgstat_wal_reset_all_cb(TimestampTz ts);
extern void pgstat_wal_snapshot_cb(void);


/*
 * pgstat_subscription.c中的函数
 */

extern bool pgstat_subscription_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
extern void pgstat_subscription_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts);

/*
 * pgstat_xact.c中的函数
 */

extern PgStat_SubXactStatus *pgstat_get_xact_stack_level(int nest_level);
extern void pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, Oid objoid);
extern void pgstat_create_transactional(PgStat_Kind kind, Oid dboid, Oid objoid);


/*
 * pgstat.c中的变量
 */

extern PGDLLIMPORT PgStat_LocalState pgStatLocal;


/*
 * 在 pgstat_slru.c 中的变量
 */

extern PGDLLIMPORT bool have_slrustats;


/*
 * 实现上面声明的内联函数。
 */

/*
 * 用于 changecount 操作的辅助工具。详见 struct
 * PgBackendStatus 周围的注释。
 */

static inline void
pgstat_begin_changecount_write(uint32 *cc)
{
	Assert((*cc & 1) == 0);

	START_CRIT_SECTION();
	(*cc)++;
	pg_write_barrier();
}

static inline void
pgstat_end_changecount_write(uint32 *cc)
{
	Assert((*cc & 1) == 1);

	pg_write_barrier();

	(*cc)++;

	END_CRIT_SECTION();
}

static inline uint32
pgstat_begin_changecount_read(uint32 *cc)
{
	uint32		before_cc = *cc;

	CHECK_FOR_INTERRUPTS();

	pg_read_barrier();

	return before_cc;
}

/*
 * 如果读取成功则返回 true，如果需要重复则返回 false。
 */
static inline bool
pgstat_end_changecount_read(uint32 *cc, uint32 before_cc)
{
	uint32		after_cc;

	pg_read_barrier();

	after_cc = *cc;

	/* 我们开始时是否正在进行写入？ */
	if (before_cc & 1)
		return false;

	/* 写入在我们读取时是否开始并完成？ */
	return before_cc == after_cc;
}


/*
 * PgStat_KindInfo->snapshot_cb 的辅助函数
 * PgStat_KindInfo->reset_all_cb 回调。
 *
 * 按照更改计数协议复制指定的内存区域。
 */
static inline void
pgstat_copy_changecounted_stats(void *dst, void *src, size_t len,
								uint32 *cc)
{
	uint32		cc_before;

	do
	{
		cc_before = pgstat_begin_changecount_read(cc);

		memcpy(dst, src, len);
	}
	while (!pgstat_end_changecount_read(cc, cc_before));
}

/* dshash / simplehash 哈希表的辅助工具 */
static inline int
pgstat_cmp_hash_key(const void *a, const void *b, size_t size, void *arg)
{
	AssertArg(size == sizeof(PgStat_HashKey) && arg == NULL);
	return memcmp(a, b, sizeof(PgStat_HashKey));
}

static inline uint32
pgstat_hash_hash_key(const void *d, size_t size, void *arg)
{
	const PgStat_HashKey *key = (PgStat_HashKey *) d;
	uint32		hash;

	AssertArg(size == sizeof(PgStat_HashKey) && arg == NULL);

	hash = murmurhash32(key->kind);
	hash = hash_combine(hash, murmurhash32(key->dboid));
	hash = hash_combine(hash, murmurhash32(key->objoid));

	return hash;
}

/*
 * 共享内存统计条目的数据部分的长度（即不包括
 * 临时数据如引用计数、lwlocks 等）。
 */
static inline size_t
pgstat_get_entry_len(PgStat_Kind kind)
{
	return pgstat_get_kind_info(kind)->shared_data_len;
}

/*
 * 返回指向共享内存统计条目的数据部分的指针。
 */
static inline void *
pgstat_get_entry_data(PgStat_Kind kind, PgStatShared_Common *entry)
{
	size_t		off = pgstat_get_kind_info(kind)->shared_data_off;

	Assert(off != 0 && off < PG_UINT32_MAX);

	return ((char *) (entry)) + off;
}

#endif							/* PGSTAT_INTERNAL_H */
